package com.ny.zmb.openapi.demo;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.ny.zmb.openapi.util.*;
import org.apache.commons.lang.StringUtils;
import org.apache.tomcat.util.codec.binary.Base64;

import java.text.SimpleDateFormat;
import java.util.*;

/**
 * @author yangx
 * @description TODO
 * @date 2021/10/9
 */
public class yxmarketDemo {

    private static final String ALLCHAR = "0123456789ABCDEF";

    public static void main(String[] args) throws Exception {

        /** 1、封装请求参数，bizContent（业务参数，需要加密）和check（需要生成密钥并加密）后面设置值*/
        Map<String,String> reqMap = new HashMap<String,String>();
        //接口方法名，根据接口文档取值
        reqMap.put("method", "openapi.yxmarket.saveRuleTemp");
        reqMap.put("timeStamp",getCurrentDateTime("yyyy-MM-dd HH:mm:dd"));
        //固定utf-8
        reqMap.put("charset","utf-8");
        reqMap.put("reqId",String.valueOf(System.currentTimeMillis()));
        //银盛商户号
//        reqMap.put("certId","826440345119153");//826440345119153测试环境可用商户号
        reqMap.put("certId", "prdtest");
        reqMap.put("version","V1.0");

        /** 2、生成对业务参数加密的密钥*/
        StringBuilder sb = new StringBuilder();
        Random random = new Random();
        for (int i = 0; i < 16; i++) {
            sb.append(ALLCHAR.charAt(random.nextInt(ALLCHAR.length())));
        }
        String key = ByteUtil.toHexString(sb.toString());

        /** 3、使用银盛公钥对密钥key进行加密，得到加密的key并设置到请求参数check中。publicFilePath为存放银盛公钥的地址*/
//        String publicFilePath = "D:/datas/test/service.cer";
        String publicFilePath = "D:/datas/prod/service.cer";
        byte[] byte1= RSA256Util.encrypt(publicFilePath, ByteUtil.hexStringToBytes(key));
        String encryptKeyCheck = Base64.encodeBase64String(byte1);
        reqMap.put("check", encryptKeyCheck);//加密后的密钥

        /** 4、封装业务参数,具体参数见文档，这里只是举例子*/
        Map<String,Object> bizContentMap = new HashMap<>();
        bizContentMap.put("name", "活动规则模板004");//规则模板名称
        bizContentMap.put("discType", "01");//优惠对象, 01：交易金额 02：手续费
        bizContentMap.put("ruleType", "01");//t1优惠类型, 01：满减
//        bizContentMap.put("d0RuleType", "01");//d0优惠类型discType为02可选, 01：满减
        //付款方式 0：普通刷卡，qr：扫码支付，1：Apple Pay，2：云闪付，withdraw：手动提现
        bizContentMap.put("payType", Arrays.asList("0","qr", "withdraw"));
        //payType取值0时cardType，cardBin选填；payType取值qr时qrPay必填
        bizContentMap.put("cardType", Arrays.asList("00","01","02"));//00：借记卡，01：贷记卡，02：准贷记卡
        bizContentMap.put("cardBin", Arrays.asList("622202","622203","622204"));
        bizContentMap.put("qrPay", Arrays.asList("2080008","2080001","2070008","2070001","2050008"));//扫码方式
        //交易金额区间
        List<Map<String, Object>> discountsList = new ArrayList<>();
        Map<String, Object> discountsMap = new HashMap<>();
        discountsMap.put("tradeMinAmount", 100.1);
        discountsMap.put("tradeMaxAmount", 1000.1);
        discountsMap.put("discount", 100.1);
        discountsMap.put("d0Discount", 100.1);
        discountsMap.put("discountMaxAmount", 100);
        discountsList.add(discountsMap);
        discountsMap = new HashMap<>();
        discountsMap.put("tradeMinAmount", 200.1);
        discountsMap.put("tradeMaxAmount", 2000.1);
        discountsMap.put("discount", 200.1);
        discountsMap.put("d0Discount", 200.1);
        discountsMap.put("discountMaxAmount", 200);
        discountsList.add(discountsMap);
        bizContentMap.put("discounts", discountsList);
        //时间规则限制（周）1-7: 星期天-星期六
        bizContentMap.put("weekCheck", Arrays.asList("2","3","4","5"));
        //时间规则限制（日期）
        bizContentMap.put("tradeDay", Arrays.asList("20211111","20211112","20211113"));
        //用户额度规则
        List<Map<String, Object>> accRuleList = new ArrayList<>();
        Map<String, Object> accRuleMap = new HashMap<>();
        accRuleMap.put("accName", "累计规则1");
        accRuleMap.put("accltObj", "mercid");
        accRuleMap.put("accltCycle", "day");
        accRuleMap.put("accCurrNum", 10);
        accRuleMap.put("accCurrAmount", 1000);
        accRuleList.add(accRuleMap);
        accRuleMap = new HashMap<>();
        accRuleMap.put("accName", "累计规则2");
        accRuleMap.put("accltObj", "mercid|payType");
        accRuleMap.put("accltCycle", "day");
        accRuleMap.put("accOption", "00,01,02");
        accRuleMap.put("accCurrNum", 10);
        accRuleMap.put("accCurrAmount", 1000);
        accRuleList.add(accRuleMap);
        bizContentMap.put("accRule", accRuleList);
        //用户额度余额不足时可抵扣部分优惠, 是：YES，否：NO
        bizContentMap.put("riskQuotaFlag", "Y");
        JSONObject bizJSONObj = JSONObject.parseObject(JSON.toJSONString(bizContentMap));

        /** 5、使用生成的密钥key对业务参数进行加密，并将加密后的业务参数放入请求参数bizContent中*/
        byte[] bte= AESUtil.encrypt(JSONObject.toJSONBytes(bizJSONObj, SerializerFeature.WriteNullStringAsEmpty), ByteUtil.hexStringToBytes(key));
        String msgBizContent = Base64.encodeBase64String(bte);
        reqMap.put("bizContent", msgBizContent);

        /** 6、将请求参数进行sort排序，生成拼接的字符床，并使用接入方私钥对请求参数进行加签，并将加签的值存入请求参数sign中*/
        //6.1 排序生成拼接字符串
        List<String> keys = new ArrayList<String>(reqMap.keySet());
        Collections.sort(keys);
        StringBuilder sb1 = new StringBuilder();
        for(String k : keys){
            if("sign".equals(k)) {
                continue;
            }
            sb1.append(k).append("=");
            sb1.append(reqMap.get(k));
            sb1.append("&");
        }
        if(sb1.length() > 0) {
            sb1.setLength(sb1.length() - 1);
        }
        //6.2使用接入方私钥对排序的请求参数加签，并存放到请求参数里面.privateFilePath:私钥地址，prvatePassword:私钥密钥
//        String privateFilePath = "D:/datas/cert/826440345119153/client.pfx";
//        String privatePassword = "123456";
        String privateFilePath = "D:/datas/prod/client.pfx";
        String privatePassword = "56tygh";
        String sign= RSA256Util.sign(sb1.toString(),privateFilePath,privatePassword);
        reqMap.put("sign",sign );

        /** 7、发送http请求获取返回结果，请求地址以文档为准*/
        try {
//            String httpUrl = "http://localhost:8089/openapi/ysMarket/saveRuleTemp";
            String httpUrl = "https://ysgate.ysepay.com/openapi/ysMarket/saveRuleTemp";
            String result = HttpRequestUtil.getDoPostResponse(httpUrl, reqMap, "UTF-8", null, 20*1000);
            byte[] res = Base64.decodeBase64(result);
            Map<String, String> resMap = (Map<String, String>) JSONObject.parse(new String(res,"UTF-8"));
            System.out.println("http请求返回的结果为:"+JSONObject.toJSONString(resMap));

            /** 8、对结果进行解密，并使用银盛公钥验签*/
            if(StringUtils.isNotBlank(resMap.get("sign"))) {
                byte[] srcData = SignUtil.getSignDataStr1(resMap).getBytes("UTF-8");
                boolean validateSignResult= RSA256Util.validateSign(publicFilePath,Base64.decodeBase64(resMap.get("sign")),srcData);
                if(!validateSignResult){
                    System.out.println("验签失败");
                    throw new Exception("验签失败");
                }
            }else{
                throw new Exception("验签失败,未返回加签信息,可能是银盛未配置发起方或者发起方证书类型配置有误,返回结果提示为:{}"+resMap.get("msg"));
            }

            /** 9、针对自己的业务，根据返回的结果的code 和 subCode 做接入方的业务处理*/
            if(!"00000".equals(resMap.get("code"))){
                System.out.println("请求失败，code不为00000");
                return;
            }
            if(!"101".equals(resMap.get("subCode"))){
                System.out.println("查询业务失败，错误信息为:"+resMap.get("subMsg"));
                return;
            }
            /** 10、使用上面生成的加密密钥key，解密返回的业务参数*/
            byte[] dataEncode=Base64.decodeBase64(resMap.get("businessData"));
            byte[] data= AESUtil.decrypt(dataEncode, ByteUtil.hexStringToBytes(key));
            String businessData=new String(data,"UTF-8");
            System.out.println("返回的业务参数为:"+businessData);
        } catch (Exception e) {
            e.printStackTrace();
            throw  e;
        }
    }

    /**
     * 格式化当前日期时间字符串
     * @return
     */
    private static  String getCurrentDateTime(String pattern){
        SimpleDateFormat sdf = new SimpleDateFormat(pattern);
        return sdf.format(new Date());
    }

}
